/* eslint-disable jsdoc/no-restricted-syntax */
/* eslint-disable prefer-destructuring */
import { Signal, SignalWatcher, signal } from "@lit-labs/signals";
import { LitElement, html } from "lit";
import { createRef, ref } from "lit/directives/ref.js";

/**
 * @rawDefine
 */
class CSSCustomHighlightAPIExample extends SignalWatcher(LitElement) {
  constructor() {
    super();
    this.query = /**
     * @type {import('lit/directives/ref.js').Ref<HTMLInputElement>}
     */ (createRef());
    this.article = /**
     * @type {import('lit/directives/ref.js').Ref<HTMLElement>}
     */ (createRef());

    this.treeWalker = (() => {
      if (this.shadowRoot) {
        /**
         * @type {Document}
         */
        const shadowRoot = /** @type {*} */ (this.shadowRoot);
        return shadowRoot.createTreeWalker(
          /** @type {Node} */ (this.article.value),
          NodeFilter.SHOW_TEXT,
        );
      }
      throw new Error("not support");
    })();
    /**
     * @type {import('@lit-labs/signals').Signal.State<Node[]>}
     */
    this.allTextNodes = signal(/** @type {Node[]} */ ([]));
    let currentNode = this.treeWalker.nextNode();
    while (currentNode) {
      this.allTextNodes.set([...this.allTextNodes.get(), currentNode]);
      currentNode = this.treeWalker.nextNode();
    }
    this.#hightLight();
  }

  /**
   * @override
   * @returns {import('lit').HTMLTemplateResult}
   */
  render() {
    return html`
      <label
        >Search within text <input id="query" type="text" ${ref(this.query)}
      /></label>
      <article ${ref(this.article)}>
        <p>
          Maxime debitis hic, delectus perspiciatis laborum molestiae labore,
          deleniti, quam consequatur iure veniam alias voluptas nisi quo.
          Dolorem eaque alias, quo vel quas repudiandae architecto deserunt
          quidem, sapiente laudantium nulla.
        </p>
        <p>
          Maiores odit molestias, necessitatibus doloremque dolor illum
          reprehenderit provident nostrum laboriosam iste, tempore perferendis!
          Ab porro neque esse voluptas libero necessitatibus fugiat, ex, minus
          atque deserunt veniam molestiae tempora? Vitae.
        </p>
        <p>
          Dolorum facilis voluptate eaque eius similique ducimus dignissimos
          assumenda quos architecto. Doloremque deleniti non exercitationem
          rerum quam alias harum, nisi obcaecati corporis temporibus vero
          sapiente voluptatum est quibusdam id ipsa.
        </p>
      </article>
    `;
  }

  #hightLight() {
    // If the CSS Custom Highlight API is not supported,
    // display a message and bail-out.
    if (!CSS.highlights) {
      if (this.article.value) {
        console.warn("CSS Custom Highlight API not supported.");
        this.article.value.textContent =
          "CSS Custom Highlight API not supported.";
      }
      return;
    }

    // Clear the HighlightRegistry to remove the
    // previous search results.
    CSS.highlights.clear();

    // Clean-up the search query and bail-out if
    // if it's empty.
    /**
     * @type {string | undefined}
     */
    const str = this.query.value?.value.trim().toLowerCase();
    if (!str) {
      return;
    }

    // Iterate over all text nodes and find matches.
    const ranges = this.allTextNodes
      .get()
      .map((el) => ({ el, text: el.textContent?.toLowerCase() ?? "" }))
      .map(({ text, el }) => {
        /**
         * @type {number[]}
         */
        const indices = [];
        let startPos = 0;
        while (startPos < text.length) {
          const index = text.indexOf(str, startPos);
          if (index === -1) break;
          indices.push(index);
          startPos = index + str.length;
        }

        // Create a range object for each instance of
        // str we found in the text node.
        return indices.map((index) => {
          const range = new Range();
          range.setStart(el, index);
          range.setEnd(el, index + str.length);
          return range;
        });
      });

    // Create a Highlight object for the ranges.
    const searchResultsHighlight = new Highlight(...ranges.flat());

    // Register the Highlight object in the registry.
    CSS.highlights.set("search-results", searchResultsHighlight);
  }
}
customElements.define("css-custom-highlight-api", CSSCustomHighlightAPIExample);

export default CSSCustomHighlightAPIExample;
